# Rewrite the Tkinter GUI with a cleaner, resizable layout and clear mode/op selectors.
# Changes:
# - Proper grid with two columns, resizable window
# - Mode/Operation radio groups clearly visible
# - Scrollbar for output
# - "显示子密钥"按钮（便于关卡1）
# - Robust high-DPI handling attempt

# -*- coding: utf-8 -*-
import tkinter as tk
from tkinter import ttk, messagebox
from typing import Tuple
import os, sys

HERE = os.path.dirname(os.path.abspath(__file__))
if HERE not in sys.path:
    sys.path.insert(0, HERE)

try:
    import sdes
except Exception as e:
    raise SystemExit("请先将 sdes.py 放在本文件同一目录下。\n错误: %s" % e)


def parse_key(text: str) -> int:
    text = text.strip().replace(" ", "")
    if text.startswith("0b"):
        k = int(text, 2)
    elif all(ch in "01" for ch in text) and 1 <= len(text) <= 10:
        k = int(text, 2)
    else:
        k = int(text, 10)
    if not (0 <= k < 1024):
        raise ValueError("密钥必须是 10 位（0..1023）。")
    return k


def parse_byte(text: str) -> int:
    t = text.strip().replace(" ", "")
    if t.startswith("0b"):
        v = int(t, 2)
    elif t.startswith("0x"):
        v = int(t, 16)
    elif all(ch in "01" for ch in t) and 1 <= len(t) <= 8:
        v = int(t, 2)
    else:
        v = int(t, 10)
    if not (0 <= v < 256):
        raise ValueError("字节必须在 0..255 范围。")
    return v


def fmt_byte(v: int) -> Tuple[str, str, str]:
    return (f"{v}", f"0b{v:08b}", f"0x{v:02X}")


class App(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("S-DES 极简演示")
        # High-DPI tweak (best-effort; safe no-op if unsupported)
        try:
            self.tk.call('tk', 'scaling', 1.25)
        except Exception:
            pass

        self.geometry("740x460")
        self.minsize(640, 420)
        self.resizable(True, True)

        self.mode = tk.StringVar(value="byte")  # byte | text
        self.op = tk.StringVar(value="enc")     # enc | dec

        # Main frame
        frm = ttk.Frame(self, padding=12)
        frm.grid(row=0, column=0, sticky="nsew")
        self.grid_rowconfigure(0, weight=1)
        self.grid_columnconfigure(0, weight=1)

        # Layout: two columns
        for c in range(2):
            frm.grid_columnconfigure(c, weight=1 if c == 1 else 0)

        # Row 0: Key
        ttk.Label(frm, text="Key (10位，十进制或二进制):").grid(row=0, column=0, sticky="w", padx=(0,8), pady=(0,6))
        self.ent_key = ttk.Entry(frm)
        self.ent_key.grid(row=0, column=1, sticky="we", pady=(0,6))
        self.ent_key.insert(0, "642")  # 0b1010000010

        # Row 1: Input label + entry
        self.lbl_in = ttk.Label(frm, text="明文字节 (十进制/二进制/十六进制):")
        self.lbl_in.grid(row=1, column=0, sticky="w", padx=(0,8), pady=(0,6))
        self.ent_in = ttk.Entry(frm)
        self.ent_in.grid(row=1, column=1, sticky="we", pady=(0,6))
        self.ent_in.insert(0, "215")

        # Row 2: Mode & Operation
        mode_frame = ttk.LabelFrame(frm, text="模式")
        mode_frame.grid(row=2, column=0, sticky="w", pady=(4,6))
        ttk.Radiobutton(mode_frame, text="字节", variable=self.mode, value="byte", command=self.on_mode).pack(side=tk.LEFT, padx=6, pady=4)
        ttk.Radiobutton(mode_frame, text="文本", variable=self.mode, value="text", command=self.on_mode).pack(side=tk.LEFT, padx=6, pady=4)

        op_frame = ttk.LabelFrame(frm, text="操作")
        op_frame.grid(row=2, column=1, sticky="w", pady=(4,6))
        ttk.Radiobutton(op_frame, text="加密", variable=self.op, value="enc", command=self.on_mode).pack(side=tk.LEFT, padx=6, pady=4)
        ttk.Radiobutton(op_frame, text="解密", variable=self.op, value="dec", command=self.on_mode).pack(side=tk.LEFT, padx=6, pady=4)

        # Row 3: Buttons
        btns = ttk.Frame(frm)
        btns.grid(row=3, column=0, columnspan=2, sticky="w", pady=(4,6))
        ttk.Button(btns, text="运行", command=self.run).pack(side=tk.LEFT)
        ttk.Button(btns, text="清空输出", command=lambda: self.txt_out.delete("1.0", tk.END)).pack(side=tk.LEFT, padx=8)
        ttk.Button(btns, text="显示子密钥 K1/K2", command=self.show_subkeys).pack(side=tk.LEFT)

        # Row 4: Output text + scrollbar
        out_frame = ttk.Frame(frm)
        out_frame.grid(row=4, column=0, columnspan=2, sticky="nsew", pady=(6,4))
        frm.grid_rowconfigure(4, weight=1)
        self.txt_out = tk.Text(out_frame, wrap="word")
        yscroll = ttk.Scrollbar(out_frame, orient="vertical", command=self.txt_out.yview)
        self.txt_out.configure(yscrollcommand=yscroll.set)
        self.txt_out.grid(row=0, column=0, sticky="nsew")
        yscroll.grid(row=0, column=1, sticky="ns")
        out_frame.grid_rowconfigure(0, weight=1)
        out_frame.grid_columnconfigure(0, weight=1)

        # Row 5: hint
        hint = ("提示: 字节模式支持十进制(215)、二进制(0b11010111或11010111)与十六进制(0xD7)。"
                "文本模式按ASCII/latin1逐字节加解密；解密需输入十六进制密文字节（如 C4F80D0D2F）。")
        ttk.Label(frm, text=hint).grid(row=5, column=0, columnspan=2, sticky="w", pady=(4,0))

    def on_mode(self):
        if self.mode.get() == "byte":
            self.lbl_in.configure(text="明文字节 (十进制/二进制/十六进制):" if self.op.get()=="enc" else "密文字节:")
            self.ent_in.delete(0, tk.END)
            self.ent_in.insert(0, "215" if self.op.get()=="enc" else "140")
        else:
            self.lbl_in.configure(text="明文字符串:" if self.op.get()=="enc" else "密文字节（十六进制，如: C4F80D0D2F）:")
            self.ent_in.delete(0, tk.END)
            self.ent_in.insert(0, "Hello" if self.op.get()=="enc" else "")

    def run(self):
        try:
            key = parse_key(self.ent_key.get())
            mode = self.mode.get()
            op = self.op.get()
            if mode == "byte":
                v = parse_byte(self.ent_in.get())
                if op == "enc":
                    out = sdes.encrypt_byte(v, key)
                    d_dec, d_bin, d_hex = fmt_byte(out)
                    self.write(f"[加密-字节]\nkey={key} (0b{key:010b})\nplain: {fmt_byte(v)}\n=> cipher: {d_dec}, {d_bin}, {d_hex}\n")
                else:
                    out = sdes.decrypt_byte(v, key)
                    d_dec, d_bin, d_hex = fmt_byte(out)
                    self.write(f"[解密-字节]\nkey={key} (0b{key:010b})\ncipher: {fmt_byte(v)}\n=> plain: {d_dec}, {d_bin}, {d_hex}\n")
            else:
                if op == "enc":
                    text = self.ent_in.get()
                    enc = sdes.encrypt_str(text, key)
                    self.write(f"[加密-文本]\nkey={key} (0b{key:010b})\nplain: {text}\n=> cipher bytes (hex): {enc.hex().upper()}\n")
                else:
                    hexs = self.ent_in.get().strip().replace(" ", "")
                    try:
                        data = bytes.fromhex(hexs)
                    except Exception:
                        raise ValueError("请输入十六进制密文字节，例如: C4F80D0D2F")
                    dec = sdes.decrypt_bytes(data, key)
                    try:
                        txt = dec.decode('latin1')
                    except Exception:
                        txt = str(list(dec))
                    self.write(f"[解密-文本]\nkey={key} (0b{key:010b})\ncipher: {hexs.upper()}\n=> plain: {txt}\n")
        except Exception as e:
            messagebox.showerror("错误", str(e))

    def show_subkeys(self):
        try:
            key = parse_key(self.ent_key.get())
            K1, K2 = sdes._subkeys(key)
            self.write(f"[子密钥]\nkey={key} (0b{key:010b})\nK1={K1}\nK2={K2}\n")
        except Exception as e:
            messagebox.showerror("错误", str(e))

    def write(self, s: str):
        self.txt_out.insert(tk.END, s + "\n")
        self.txt_out.see(tk.END)


if __name__ == "__main__":
    App().mainloop()

